package jungnetwork;

/* This is a simple display capable of working with MASON and 
 * displaying JUNG graphs. It is composed of merged and 
 * lobotomized verions of JUNG's ZoomDemo and MASON's Display2D 
 * classes. Please refer to original code for details about uncommented code.   */

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.FileDialog;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import sim.display.Display2D;
import sim.display.GUIState;
import sim.display.SimApplet;
import sim.engine.SimState;
import sim.engine.Steppable;
import sim.engine.Stoppable;
import sim.util.Utilities;
import sim.util.media.PngEncoder;
import edu.uci.ics.jung.visualization.BirdsEyeVisualizationViewer;
import edu.uci.ics.jung.visualization.Layout;
import edu.uci.ics.jung.visualization.PluggableRenderer;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.contrib.KKLayout;
import edu.uci.ics.jung.visualization.control.ScalingControl;
import edu.uci.ics.jung.visualization.control.ViewScalingControl;

public class JungDisplay extends JComponent implements Steppable {

    public static final ImageIcon CAMERA_ICON = iconFor("Camera.png");

    public static final ImageIcon CAMERA_ICON_P = iconFor("CameraPressed.png");

    private static final long serialVersionUID = 1L;

    public Layout layout;

    public VisualizationViewer viewer;

    public JFrame frame;

    /* Variable which will keep track of time of next update */
    public double nextUpdate = 0;

    GUIState simulation;

    JDialog dialog;

    Stoppable stopper;

    Box header;

    /* The button which snaps a screenshot */
    JButton snapshotButton;

    /* Set to true if we're running on a Mac */
    public static final boolean isMacOSX = isMacOSX();

    /* Set to true if we're running on Windows */
    public static final boolean isWindows = isWindows();

    static ImageIcon iconFor(String name) {
        return new ImageIcon(Display2D.class.getResource(name));
    }

    public void step(SimState state) {

        double currentTime = simulation.state.schedule.time();

        /*
         * Check if it is apropriate time to update, draw only if the display
         * can be seen.
         */

        if ((currentTime > nextUpdate)) {

            if (frame.isShowing()) {

                layout.restart();
                while (!layout.incrementsAreDone()) {
                    layout.advancePositions();
                    }
                viewer.repaint();
                }

            // Compute nextUpdate time.

            nextUpdate = currentTime + ((PreferentialAttachment) simulation.state).updateInterval;
            }
    }

    public JungDisplay(GUIState simulation) {

        this.simulation = simulation;
        this.frame = new JFrame();

        /*
         * Layout is the JUNG's procedure which plans how the graph will be
         * drawn. Check out other layouts, by default we will use Kamada-Kawai.
         */
        layout = new KKLayout(((GraphSimState) simulation.state).graph);

        /*
         * Viewer is AWT Component subclass, doing the rendering, which can be
         * directly added to a JPanel.
         */
        viewer = new VisualizationViewer(layout, new PluggableRenderer());

        Container content = frame.getContentPane();
        JPanel panel = new JPanel(new BorderLayout());
        panel.add(viewer);
        content.add(panel);

        /*
         * Additional BirdsEyeView for zooming / panning the graph will be
         * created. See JUNG's ZoomDemo for explanations.
         */

        dialog = new JDialog(frame);
        content = dialog.getContentPane();

        final BirdsEyeVisualizationViewer bird = new BirdsEyeVisualizationViewer(viewer, 0.25f, 0.25f);

        JButton reset = new JButton("No Zoom");
        reset.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                bird.resetLens();
            }
            });
        final ScalingControl scaler = new ViewScalingControl();
        JButton plus = new JButton("+");
        plus.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                scaler.scale(viewer, 1.1f, viewer.getCenter());
            }
            });
        JButton minus = new JButton("-");
        minus.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                scaler.scale(viewer, 0.9f, viewer.getCenter());
            }
            });
        JButton help = new JButton("Help");
        help.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                String zoomHelp = "<html><center>Drag the rectangle to pan<p>" + "Drag one side of the rectangle to zoom</center></html>";
                JOptionPane.showMessageDialog(dialog, zoomHelp);
            }
            });
        JPanel controls = new JPanel(new GridLayout(2, 2));
        controls.add(plus);
        controls.add(minus);
        controls.add(reset);
        controls.add(help);
        content.add(bird);
        content.add(controls, BorderLayout.SOUTH);

        JButton zoomer = new JButton("Show Zoom Window");
        zoomer.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                dialog.pack();
                dialog.setLocation((int) (frame.getLocationOnScreen().getX() + frame.getWidth()), (int) frame.getLocationOnScreen().getY());
                dialog.show();
                bird.initLens();
            }
            });

        header = new Box(BoxLayout.X_AXIS);
        JPanel p = new JPanel();
        p.add(zoomer);
        header.add(p);

        // Add the snapshot button for making pictures of graph.
        snapshotButton = new JButton(CAMERA_ICON);
        snapshotButton.setPressedIcon(CAMERA_ICON_P);
        snapshotButton.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
        snapshotButton.setToolTipText("Create a snapshot (as a PNG file)");
        snapshotButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                takeSnapshot();
            }
            });
        header.add(snapshotButton);

        frame.getContentPane().add(header, BorderLayout.NORTH);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(600, 600);
        frame.show();

    }

    public void reset() {

        if (stopper != null) {
            stopper.stop();
            }

        stopper = simulation.scheduleImmediateRepeat(true, this);
        this.nextUpdate = 0;

    }

    public void quit() {
        if (stopper != null)
            stopper.stop();
        stopper = null;
    }

    static boolean isMacOSX() {
        try // we'll try to get certain properties if the security permits it
            {
            return (System.getProperty("mrj.version") != null); // Apple's
            // official approach
            } catch (Throwable e) {
                return false;
                } // Non-Mac Web browsers will fail here
    }

    static boolean isWindows() {
        try // we'll try to get certain properties if the security permits it
            {
            return !isMacOSX() && (System.getProperty("os.name").startsWith("Win"));
            } catch (Throwable e) {
                return false;
                }
    }

    static String getVersion() {
        try {
            return System.getProperty("java.version");
            } catch (Throwable e) {
                return "unknown";
                }
    }

    public void takeSnapshot() {
        if (SimApplet.isApplet) {
            Object[] options = { "Oops" };
            JOptionPane.showOptionDialog(this, "You cannot save snapshots from an applet.", "MASON Applet Restriction", JOptionPane.OK_OPTION,
                                         JOptionPane.ERROR_MESSAGE, null, options, options[0]);
            return;
            }

        int width = viewer.getSize().width;

        System.out.println(width);

        int height = viewer.getSize().height;
        Color bg = getBackground();

        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
        Graphics2D graphics = bi.createGraphics();
        graphics.setColor(bg);
        graphics.fillRect(0, 0, width, height);
        viewer.paint(graphics);

        // NOW pop up the save window
        FileDialog fd = new FileDialog(this.frame, "Save Snapshot as 24-bit PNG...", FileDialog.SAVE);
        fd.setFile("Untitled.png");
        fd.setVisible(true);

        if (fd.getFile() != null)
            try {
                OutputStream stream = new BufferedOutputStream(new FileOutputStream(new File(fd.getDirectory(), Utilities.ensureFileEndsWith(fd.getFile(),
                                                                                                                                             ".png"))));
                PngEncoder tmpEncoder = new PngEncoder(bi, false, PngEncoder.FILTER_NONE, 9);
                stream.write(tmpEncoder.pngEncode());
                stream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                    }
    }

    }
